﻿using gt.framework.Cache;
using gt.webapi.core.Entity.Common;
using gt.webapi.core.Entity.Throttle;
using System;
using System.Collections.Generic;

namespace gt.webapi.core.Repositories.Impl
{
    public class MemoryRateLimitRepository : IRateLimitRepository
    {
        public bool CheckRateLimie(string appkey)
        {
            var rule = CacheManager.Get<Dictionary<RateLimitPeriod, int>>(BuildKey(appkey));
            if (rule != null)
            {
                foreach (var kv in rule)
                {
                    var periodKey = BuildPeriodKey(appkey, kv.Key);
                    if (CacheManager.Contains(periodKey))
                    {
                        var rate = CacheManager.Get<int>(periodKey);
                        if (rate > kv.Value) return false;
                    }
                }
            }
            return true;
        }

        private static object _obj = new object();
        public void IncrementRate(string appkey)
        {
            var rule = CacheManager.Get<Dictionary<RateLimitPeriod, int>>(BuildKey(appkey));
            if (rule != null)
            {
                lock (_obj)
                {
                    foreach (var period in rule.Keys)
                    {
                        var periodKey = BuildPeriodKey(appkey, period);
                        var periodValue = 1;
                        if (CacheManager.Contains(periodKey))
                        {
                            periodValue = CacheManager.Get<int>(periodKey);
                            periodValue++;
                            CacheManager.Insert<int>(periodKey, periodValue);
                        }
                        CacheManager.Insert<int>(periodKey, periodValue, GetExpiredTime(period));
                    }
                }
            }
        }

        public void SupplerRateLimitRule(string appkey, Dictionary<RateLimitPeriod, int> rule)
        {
            var key = BuildKey(appkey);
            CacheManager.Insert<Dictionary<RateLimitPeriod, int>>(key, rule);
        }

        private string BuildKey(string appkey)
        {
            return string.Concat(Constants.KEY_RATELIMIT_PREFIX, appkey);
        }
        private string BuildPeriodKey(string appkey, RateLimitPeriod period)
        {
            return string.Concat(Constants.KEY_RATELIMIT_PERIOD_PREFIX, appkey, "_", (int)period);
        }
        private TimeSpan GetExpiredTime(RateLimitPeriod period)
        {
            DateTime now = DateTime.Now;
            TimeSpan time;
            switch (period)
            {
                case RateLimitPeriod.Minute:
                    time = TimeSpan.FromMinutes(now.Minute + 1);
                    break;
                case RateLimitPeriod.Hour:
                    time = TimeSpan.FromMinutes(now.Hour + 1);
                    break;
                case RateLimitPeriod.Day:
                    time = TimeSpan.FromMinutes(now.Day + 1);
                    break;
                default:
                    throw new InvalidOperationException("RateLimitPeriod");
            }
            return time;
        }
    }
}
